﻿/*
COPYRIGHT NOTICE

Copyright (c) 2011, S James S Stapleton
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
	  this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ConfigFileManager;
using System.IO;

namespace ConfigFileExplorer
{
    public partial class ConfigFileExplorer : Form
    {
        protected internal ToolTip tt_all;
        internal List<add_ui_elements> add_elements;
        /// <summary>
        /// true of the kvpairs tab had a visible scroll
        /// </summary>
        protected internal bool kvp_scroll_wasvis;
        static protected internal ConfigFileExplorer me;
        FileDialog m_fd;

        public ConfigFileExplorer()
        {
            InitializeComponent();
            this.tt_all = new ToolTip();
            this.tt_all.ShowAlways = false;
            this.tt_all.SetToolTip(this.btn_loadfile,           "Load configuration from file to current tab");
            this.tt_all.SetToolTip(this.btn_loadarr,            "Load configuration from file through an array to current tab");
            this.tt_all.SetToolTip(this.btn_savefile,           "Save configuration to file from key/value tab");
            this.tt_all.SetToolTip(this.btn_save_multi,         "Save configuration to file from key/value tab (keep multiple assignments)");
            this.tt_all.SetToolTip(this.btn_opendir,            "Find a config file (ignore overwrite warning if it pops up)");
            this.tt_all.SetToolTip(this.btn_load_comments,      "Load configuration and comments from file to current tab");
            this.tt_all.SetToolTip(this.btn_load_comments_arr, "Load configuration and comments from file through an array to current tab");
            this.tt_all.SetToolTip(this.btn_load_parts,        "Load configuration and comments from file to current tab as multi-valued");
            this.tt_all.SetToolTip(this.btn_load_partsarr, "Load configuration and comments from file through an array to current tab as multi-valued");
            this.add_elements = new List<add_ui_elements>();
            kvp_scroll_wasvis = false;
            me = this;
            m_fd = new SaveFileDialog();
            m_fd.CheckFileExists = false;
            m_fd.CheckPathExists = true;
            m_fd.Title = "Select a Config File";
            m_fd.Filter = "Config Files|*.cfg|All Files|*.*";
        }

        private void btn_add_Click(object sender, EventArgs e)
        {
            add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
            add_elements.Add(aue);
        }

        private void btn_savefile_Click(object sender, EventArgs e)
        {
            string fname = txt_file.Text;
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("Please enter a file in the top text box");
                return;
            }


            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                Dictionary<string, string> pairs = new Dictionary<string, string>();
                Dictionary<string, string> comments = new Dictionary<string, string>();
                string[] order = new string[add_elements.Count];

                int i = 0;
                List<int> not_loaded = new List<int>();
                foreach(add_ui_elements aue in add_elements)
                {
                    string key = aue.m_key.Text;
                    string val = aue.m_val.Text;
                    string com = aue.m_com.Text;
                    if(string.IsNullOrEmpty(key))
                    {
                        i++;
                        not_loaded.Add(i);
                        continue;
                    }
                    if(val == null) val = "";
                    if(!pairs.ContainsKey(key)) pairs[key] = "";
                    pairs[key] += val;

                    if(!string.IsNullOrEmpty(com))
                    {
                        if(!comments.ContainsKey(key)) comments[key] = "";
                        comments[key] = com;
                    }
                    order[i] = key;
                    i++;
                }

                if(not_loaded.Count > 0)
                {
                    string msg = "The following pairs (1 indexed) were not set, continue?\n";
                    bool first = true;
                    foreach(int v in not_loaded)
                    {
                        if(!first) msg += ", ";
                        else first = false;
                        msg += v.ToString();
                    }

                    if(MessageBox.Show(msg, "Continue?", MessageBoxButtons.YesNo) == DialogResult.No)
                        return;
                }

                try
                {
                    ConfigFile.Write(fname, pairs, comments, order);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error Writing file: " + ex.GetType().ToString() + ":" + ex.Message, "File Save Error");
                }
            }
            else
            {
                byte[] outp = ConfigFile.default_encoding.GetBytes(this.txt_file_editor.Text);

                using(FileStream fp = new FileStream(fname, FileMode.Create, FileAccess.Write))
                {
                    fp.Write(outp, 0, outp.Length);
                }
            }
        }

        private void btn_save_multi_Click(object sender, EventArgs e)
        {
            string fname = txt_file.Text;
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("Please enter a file in the top text box");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                Dictionary<string, IList<string>> pairs = new Dictionary<string, IList<string>>();
                Dictionary<string, IList<string>> comments = new Dictionary<string, IList<string>>();
                IList<string> order = new List<string>(add_elements.Count);

                int i = 0;
                List<int> not_loaded = new List<int>();
                foreach(add_ui_elements aue in add_elements)
                {
                    string key = aue.m_key.Text;
                    string val = aue.m_val.Text;
                    string com = aue.m_com.Text;
                    if(string.IsNullOrEmpty(key))
                    {
                        i++;
                        not_loaded.Add(i);
                        continue;
                    }
                    if(val == null) val = "";
                    if(!pairs.ContainsKey(key))
                    {
                        pairs[key] = new List<string>();
                        comments[key] = new List<string>();
                    }
                    pairs[key].Add(val);

                    if(com != null)
                        comments[key].Add(com);
                    else
                        comments[key].Add("");
                    order.Add(key);
                    i++;
                }

                if(not_loaded.Count > 0)
                {
                    string msg = "The following pairs (1 indexed) were not set, continue?\n";
                    bool first = true;
                    foreach(int v in not_loaded)
                    {
                        if(!first) msg += ", ";
                        else first = false;
                        msg += v.ToString();
                    }

                    if(MessageBox.Show(msg, "Continue?", MessageBoxButtons.YesNo) == DialogResult.No)
                        return;
                }

                try
                {
                    ConfigFile.Write(fname, pairs, comments, order);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error Writing file: " + ex.GetType().ToString() + ":" + ex.Message, "File Save Error");
                }
            }
            else
            {
                byte[] outp = ConfigFile.default_encoding.GetBytes(this.txt_file_editor.Text);

                using(FileStream fp = new FileStream(fname, FileMode.Create, FileAccess.Write))
                {
                    fp.Write(outp, 0, outp.Length);
                }
            }
        }

        private void btn_opendir_Click(object sender, EventArgs e)
        {
            //get a file
            if(DialogResult.Cancel == this.m_fd.ShowDialog()) return;
            this.txt_file.Text = this.m_fd.FileName;
        }

        private void btn_loadfile_Click(object sender, EventArgs e)
        {
            string fname = this.txt_file.Text;
            Dictionary<string, string> outd = new Dictionary<string,string>();
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("File must be set", "File load error");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                //remove kvs loaded
                if(add_elements.Count > 0)
                {
                    Control pframe = add_elements[0].m_parent;
                    this.SuspendLayout();
                    pframe.SuspendLayout();
                    while(add_elements.Count > 0)
                    {
                        add_elements[0].remove_this(false);
                    }
                    pframe.ResumeLayout(false);
                    pframe.PerformLayout();
                    this.ResumeLayout(false);
                    this.PerformLayout();
                }

                try
                {
                    ConfigFile.Read(fname, ref outd);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                    return;
                }
                foreach(string key in outd.Keys)
                {
                    add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
                    add_elements.Add(aue);
                    aue.m_key.Text = key;
                    aue.m_val.Text = outd[key];
                }
            }
            else
            {
                LoadFileToEditor(fname);
            }
        }

        private void btn_loadarr_Click(object sender, EventArgs e)
        {
            string fname = this.txt_file.Text;
            Dictionary<string, string> outd = new Dictionary<string, string>();
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("File must be set", "File load error");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                string[] text;

                //remove kvs loaded
                if(add_elements.Count > 0)
                {
                    Control pframe = add_elements[0].m_parent;
                    this.SuspendLayout();
                    pframe.SuspendLayout();
                    while(add_elements.Count > 0)
                    {
                        add_elements[0].remove_this(false);
                    }
                    pframe.ResumeLayout(false);
                    pframe.PerformLayout();
                    this.ResumeLayout(false);
                    this.PerformLayout();
                }

                try
                {
                    using(FileStream fp = new FileStream(fname, FileMode.Open, FileAccess.Read))
                    {
                        byte[] readbuff = new byte[fp.Length];
                        int pos = 0;
                        while(pos < fp.Length)
                            pos += fp.Read(readbuff, pos, (int)fp.Length - pos);
                        text = ConfigFile.default_encoding.GetString(readbuff).Split(ConfigFile.c_newlines, StringSplitOptions.None);
                    }
                    ConfigFile.Read(fname, ref outd, text);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                    return;
                }
                foreach(string key in outd.Keys)
                {
                    add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
                    add_elements.Add(aue);
                    aue.m_key.Text = key;
                    aue.m_val.Text = outd[key];
                }
            }
            else
            {
                LoadFileToEditor(fname);
            }
        }

        private void btn_load_comments_Click(object sender, EventArgs e)
        {
            string fname = this.txt_file.Text;
            Dictionary<string, string> outd = new Dictionary<string, string>();
            Dictionary<string, string> comments = new Dictionary<string, string>();
            string[] order;
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("File must be set", "File load error");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                //remove kvs loaded
                if(add_elements.Count > 0)
                {
                    Control pframe = add_elements[0].m_parent;
                    this.SuspendLayout();
                    pframe.SuspendLayout();
                    while(add_elements.Count > 0)
                    {
                        add_elements[0].remove_this(false);
                    }
                    pframe.ResumeLayout(false);
                    pframe.PerformLayout();
                    this.ResumeLayout(false);
                    this.PerformLayout();
                }

                try
                {
                    ConfigFile.Read(fname, out order, ref outd, ref comments);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                    return;
                }
                foreach(string key in order)
                {
                    add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
                    add_elements.Add(aue);
                    aue.m_key.Text = key;
                    aue.m_val.Text = outd[key];
                    if(comments.ContainsKey(key)) aue.m_com.Text = comments[key];
                }
            }
            else
            {
                LoadFileToEditor(fname);
            }
        }

        private void btn_load_comments_arr_Click(object sender, EventArgs e)
        {
            string fname = this.txt_file.Text;
            Dictionary<string, string> outd = new Dictionary<string, string>();
            Dictionary<string, string> comments = new Dictionary<string, string>();
            string[] order;
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("File must be set", "File load error");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                string[] text;

                //remove kvs loaded
                if(add_elements.Count > 0)
                {
                    Control pframe = add_elements[0].m_parent;
                    this.SuspendLayout();
                    pframe.SuspendLayout();
                    while(add_elements.Count > 0)
                    {
                        add_elements[0].remove_this(false);
                    }
                    pframe.ResumeLayout(false);
                    pframe.PerformLayout();
                    this.ResumeLayout(false);
                    this.PerformLayout();
                }

                try
                {
                    using(FileStream fp = new FileStream(fname, FileMode.Open, FileAccess.Read))
                    {
                        byte[] readbuff = new byte[fp.Length];
                        int pos = 0;
                        while(pos < fp.Length)
                            pos += fp.Read(readbuff, pos, (int)fp.Length - pos);
                        text = ConfigFile.default_encoding.GetString(readbuff).Split(ConfigFile.c_newlines, StringSplitOptions.None);
                    }
                    ConfigFile.Read(fname, out order, ref outd, ref comments, text);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                    return;
                }
                foreach(string key in order)
                {
                    add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
                    add_elements.Add(aue);
                    aue.m_key.Text = key;
                    aue.m_val.Text = outd[key];
                    if(comments.ContainsKey(key)) aue.m_com.Text = comments[key];
                }
            }
            else
            {
                LoadFileToEditor(fname);
            }
        }

        private void btn_load_parts_Click(object sender, EventArgs e)
        {
            string fname = this.txt_file.Text;
            Dictionary<string, IList<string>> outd = new Dictionary<string, IList<string>>();
            Dictionary<string, IList<string>> comments = new Dictionary<string, IList<string>>();
            IList<string> order;
            Dictionary<string, int> loaded_to = new Dictionary<string, int>();
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("File must be set", "File load error");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                //remove kvs loaded
                if(add_elements.Count > 0)
                {
                    Control pframe = add_elements[0].m_parent;
                    this.SuspendLayout();
                    pframe.SuspendLayout();
                    while(add_elements.Count > 0)
                    {
                        add_elements[0].remove_this(false);
                    }
                    pframe.ResumeLayout(false);
                    pframe.PerformLayout();
                    this.ResumeLayout(false);
                    this.PerformLayout();
                }

                try
                {
                    ConfigFile.Read(fname, out order, ref outd, ref comments);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                    return;
                }
                //prepare the index so we can preserve output order.
                loaded_to = new Dictionary<string, int>(outd.Count);
                foreach(string key in outd.Keys)
                {
                    loaded_to[key] = 0;
                }

                foreach(string key in order)
                {
                    add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
                    int p = loaded_to[key];
                    add_elements.Add(aue);
                    aue.m_key.Text = key;
                    aue.m_val.Text = outd[key][p];
                    if(comments.ContainsKey(key))
                    {
                        IList<string> isk = comments[key];
                        aue.m_com.Text = isk[p];
                    }
                    loaded_to[key]++;
                }
            }
            else
            {
                LoadFileToEditor(fname);
            }
        }

        private void btn_load_partsarr_Click(object sender, EventArgs e)
        {
            string fname = this.txt_file.Text;
            Dictionary<string, IList<string>> outd = new Dictionary<string, IList<string>>();
            Dictionary<string, IList<string>> comments = new Dictionary<string, IList<string>>();
            IList<string> order;
            Dictionary<string, int> loaded_to = new Dictionary<string, int>();
            string[] text;
            if(string.IsNullOrEmpty(fname))
            {
                MessageBox.Show("File must be set", "File load error");
                return;
            }

            if(tabc_cfgvals.SelectedTab == this.tabp_kvpairs)
            {
                //remove kvs loaded
                if(add_elements.Count > 0)
                {
                    Control pframe = add_elements[0].m_parent;
                    this.SuspendLayout();
                    pframe.SuspendLayout();
                    while(add_elements.Count > 0)
                    {
                        add_elements[0].remove_this(false);
                    }
                    pframe.ResumeLayout(false);
                    pframe.PerformLayout();
                    this.ResumeLayout(false);
                    this.PerformLayout();
                }


                try
                {
                    using(FileStream fp = new FileStream(fname, FileMode.Open, FileAccess.Read))
                    {
                        byte[] readbuff = new byte[fp.Length];
                        int pos = 0;
                        while(pos < fp.Length)
                            pos += fp.Read(readbuff, pos, (int)fp.Length - pos);
                        text = ConfigFile.default_encoding.GetString(readbuff).Split(ConfigFile.c_newlines, StringSplitOptions.None);
                    }
                    ConfigFile.Read(fname, out order, ref outd, ref comments, text);
                }
                catch(Exception ex)
                {
                    MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                    return;
                }
                //prepare the index so we can preserve output order.
                loaded_to = new Dictionary<string, int>(outd.Count);
                foreach(string key in outd.Keys)
                {
                    loaded_to[key] = 0;
                }

                foreach(string key in order)
                {
                    add_ui_elements aue = new add_ui_elements(this.tabp_kvpairs, add_elements.Count);
                    int p = loaded_to[key];
                    add_elements.Add(aue);
                    aue.m_key.Text = key;
                    aue.m_val.Text = outd[key][p];
                    if(comments.ContainsKey(key))
                    {
                        IList<string> isk = comments[key];
                        aue.m_com.Text = isk[p];
                    }
                    loaded_to[key]++;
                }
            }
            else
            {
                LoadFileToEditor(fname);
            }
        }

        /// <summary>
        /// Load the file to the editor
        /// </summary>
        /// <param name="s">The path to the file, this is not validated.</param>
        public void LoadFileToEditor(string s)
        {
            try
            {
                using(FileStream fp = new FileStream(s, FileMode.Open, FileAccess.Read))
                {
                    byte[] readbuff = new byte[fp.Length];
                    int pos = 0;
                    while(pos < fp.Length)
                        pos += fp.Read(readbuff, pos, (int)fp.Length - pos);
                    string txt = ConfigFile.default_encoding.GetString(readbuff);
                    txt = txt.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); // different systems, different linebreaks.
                    this.txt_file_editor.Text = txt;
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show("Error reading file: " + ex.GetType().ToString() + ":" + ex.Message, "File Read Error");
                return;
            }
        }
    }
}
